/*
 * @Author: “ygnnli” “1585923072@qq.com”
 * @Date: 2024-08-03 16:18:53
 * @LastEditors: “ygnnli” “1585923072@qq.com”
 * @LastEditTime: 2024-08-04 10:29:09
 * @FilePath: /110/46/udp_echo_server/UdpClient.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include "Thread.hpp"
#include "Comm.hpp"

using namespace ThreadModule;

// 输入格式不对
void Usage(std::string proc)
{
    std::cout << "Usage: \n\t" << proc << "loacl_ip  local_port\n"
              << std::endl;
}

// 初始化客户端
int InitClient(std::string &serverip, uint16_t &serverport, struct sockaddr_in *server)
{
    // 1. 创建套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        std::cerr << "socket error" << std::endl;
        return -1;
    }

    // 2. client要不要bind？一定要，client也要有自己的IP和PORT。要不要显式[和server一样用bind函数]的bind？不能！不建议！！
    // a. 如何bind呢？udp client首次发送数据的时候，OS会自己自动随机的给client进行bind --- 为什么？防止client port冲突。要bind，必然要和port关联！
    // b. 什么时候bind呢？首次发送数据的时候

    // 构建目标主机的socket信息
    memset(server, 0, sizeof(server));
    server->sin_family = AF_INET;
    server->sin_port = htons(serverport);
    server->sin_addr.s_addr = inet_addr(serverip.c_str());

    return sockfd;
}
// 接收消息
void recvmessage(int sockfd, std::string name)
{
    // int fd = OpenDev("/dev/pts/5", O_WRONLY);
    while (1)
    {
        // 3. 接收数据
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        char buffer[1024];
        ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
        if (n > 0)
        {
            buffer[n] = 0;
            fprintf(stderr, "[%s] %s\n", name.c_str(), buffer); // 向标准错误输出流中写入数据  使用：./udpclient 127.0.0.1 8888 2>/dev/pts/5
            // write(fd, buffer, strlen(buffer));
        }
    }
}

// 发送消息
void sendmessage(int sockfd, struct sockaddr_in &server, std::string name)
{
    std::string message;
    while (1)
    {
        printf("%s | Enter# ", name.c_str());
        fflush(stdout);
        std::getline(std::cin, message); // 从特定的流中读取数据，直到遇到换行符为止
        sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
    }
}

// ./udpclient serverip  serverport
int main(int arg, char *argv[])
{
    if (arg != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    struct sockaddr_in serveraddr;
    // 初始化
    int sockfd = InitClient(serverip, serverport, &serveraddr);
    if (sockfd == -1)
        return 1;

    func_t r = std::bind(&recvmessage, sockfd, std::placeholders::_1);
    func_t s = std::bind(&sendmessage, sockfd, serveraddr, std::placeholders::_1);

    // 创建两个线程
    Thread Recver(r, "recver");
    Thread Sender(s, "sender");

    Recver.Start();
    Sender.Start();

    Recver.Join();
    Sender.Join();

    return 0;
}